package com.tomvian.game.control
{
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
	import flash.utils.clearTimeout;
	import flash.utils.getTimer;
	import flash.utils.setTimeout;
	import sfb.engines.core.TickManager;
	
	/**
	 * ControlsManager
	 * @author Thomas Vian
	 */
	public class ControlsManager
	{
		//--------------------------------------------------------------------------
		//
		//  Properties
		//
		//--------------------------------------------------------------------------
		
		private static var inst:ControlsManager;
		public static function get instance():ControlsManager { return inst ? inst : inst = new ControlsManager(); }
		
		private var _controllers:			Array;
		private var _controllersNum:		uint;
		
		private var _keyDownList:			Vector.<Boolean>;
		
		private var _singlePressListeners:	Vector.<PressListenerGroup>;
		private var _doublePressListeners:	Vector.<PressListenerGroup>;
		private var _holdPressListeners:	Vector.<PressListenerGroup>;
		
		private var _lastKey:				uint;
		private var _doublePressTime:		Number;
		private var _lastPressTime:			Number;
		
		private var _keyPressTick:			uint;
		private var _tickManager:			TickManager;
		
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------
		
		public function ControlsManager()
		{
			_controllers = [];
			
			_keyDownList = 			new Vector.<Boolean>(256, true);
			
			_singlePressListeners = new Vector.<PressListenerGroup>(256, true);
			_doublePressListeners = new Vector.<PressListenerGroup>(256, true);
			_holdPressListeners = 	new Vector.<PressListenerGroup>(256, true);
			
			for (var i:uint = 0; i < 256; ++i)
			{
				_keyDownList[i] = false;
				
				_singlePressListeners[i] = 	new PressListenerGroup();
				_doublePressListeners[i] = 	new PressListenerGroup();
				_holdPressListeners[i] = 	new PressListenerGroup();
			}
			
			_lastKey = 0;
			_doublePressTime = 200;
			_lastPressTime = getTimer();
			
			_tickManager = TickManager.instance;
			_tickManager.addTickCall(unKeyPress, int.MIN_VALUE);
		}
		
		public function init(stage:Stage):void
		{
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownListener);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyUpListener);
		}
		
		//--------------------------------------------------------------------------
		//
		//  Single Press Methods
		//
		//--------------------------------------------------------------------------
		
		/**
		 * Register a key with a listener function for the key press event
		 * @param	key				Key to listen to
		 * @param	listener		The function to call on the key press
		 * @param	double			If the function should be called only after a double press
		 * @param	holdRepeat		If the function should be repeated when the key is held
		 */
		public function registerKeyPress(key:String, listener:Function, double:Boolean = false, holdRepeat:Boolean = false):void
		{
			var code:uint = getKeyCode(key);
			
			if (code > 0)
			{
					 if (double)		_doublePressListeners[code].addListener(listener);
				else if (holdRepeat) 	_holdPressListeners[code].addListener(listener);
				else 					_singlePressListeners[code].addListener(listener);
			}
		}
		
		/**
		 * Remoeves the listener on the key
		 * @param	key				Key to listen to
		 * @param	listener		The function to call on the key press
		 * @param	double			If the function should be called only after a double press
		 * @param	holdRepeat		If the function should be repeated when the key is held
		 */
		public function unregisterKeyPress(key:String, listener:Function, double:Boolean = false, holdRepeat:Boolean = false):void
		{
			var code:uint = getKeyCode(key);
			
			if (code > 0)
			{
					 if (double)		_doublePressListeners[code].removeListener(listener);
				else if (holdRepeat) 	_holdPressListeners[code].removeListener(listener);
				else 					_singlePressListeners[code].removeListener(listener);
			}
		}
		
		public function clearKeyPresses():void
		{
			for(var i:int = 0; i < 256; i++)
			{
				_singlePressListeners[i].clearListeners();
				_doublePressListeners[i].clearListeners();
				_holdPressListeners[i].clearListeners();
			}
		}
		
		//--------------------------------------------------------------------------
		//
		//  Controller Methods
		//
		//--------------------------------------------------------------------------
		
		public function registerController(controller:Controller):void
		{
			_controllers.push(controller);
			_controllersNum++;
		}
		
		public function unregisterController(controller:Controller):void
		{
			for (var i:uint = 0; i < _controllersNum; ++i)
			{
				if (_controllers[i] == controller)
				{
					_controllers.splice(i, 1);
					_controllersNum--;
					i--;
				}
			}
		}
		
		public function clearControllers():void
		{
			_controllers = [];
			_controllersNum = 0;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Listener Methods
		//
		//--------------------------------------------------------------------------
		
		private function keyDownListener(e:KeyboardEvent):void
		{
			var code:uint = e.keyCode;
			
			if (code < 256)
			{
				for (var i:uint = 0; i < _controllersNum; ++i)
				{
					var controller:Controller = _controllers[i];
					if (controller.keyEnabled)
					{
						var names:Array = controller.keyList[code];
						if (names)
						{
							controller.lastKey = { };
							for (var j:uint = 0, l:uint = names.length; j < l; j++)
							{
								if(!controller[names[j]]) controller.keyPress[names[j]] = true;
								
								controller[names[j]] = true;
							}
						}
					}
				}
				
				var tapTime:Number = getTimer() - _lastPressTime;
				_lastPressTime = getTimer();
				
				_keyPressTick = _tickManager.tick;
				
				if(!_keyDownList[code]) 
				{
					if (_lastKey == code && tapTime < _doublePressTime) 
					{
						_doublePressListeners[code].call();
						_lastPressTime = 0;
					}
					else 
					{
						_singlePressListeners[code].call();
					}
					_lastKey = code;
				}
				else
				{
					_holdPressListeners[code].call();
				}
				
				_keyDownList[code] = true;
			}
		}
		
		public function unKeyPress():void
		{
			if (_tickManager.tick - _keyPressTick > 1)
			{
				for (var i:uint = 0; i < _controllersNum; ++i)
				{
					_controllers[i].keyPress = { };
				}
			}
		}
		
		private function keyUpListener(e:KeyboardEvent):void
		{
			var code:uint = e.keyCode;
			
			if (code < 256)
			{
				_keyDownList[code] = false;
				
				for (var i:uint = 0; i < _controllersNum; ++i)
				{
					var controller:Controller = _controllers[i];
					if (controller.keyEnabled)
					{
						var names:Array = controller.keyList[code];
						if (names)
						{
							for (var j:uint = 0, l:uint = names.length; j < l; j++)
							{
								controller[names[j]] = false;
							}
						}
					}
				}
			}
		}
		
		//--------------------------------------------------------------------------
		//
		//  Util Methods
		//
		//--------------------------------------------------------------------------
		
		public function getKeyCode(key:String):uint
		{
			var code:uint = Keyboard[key.toUpperCase()];
			
			if (code == 0)
			{
				switch(key.toLowerCase())
				{
					case "a": {code = 65; break;} case "b": {code = 66; break;} case "c": {code = 67; break;}
					case "d": {code = 68; break;} case "e": {code = 69; break;} case "f": {code = 70; break;}
					case "g": {code = 71; break;} case "h": {code = 72; break;} case "i": {code = 73; break;}
					case "j": {code = 74; break;} case "k": {code = 75; break;} case "l": {code = 76; break;}
					case "m": {code = 77; break;} case "n": {code = 78; break;} case "o": {code = 79; break;}
					case "p": {code = 80; break;} case "q": {code = 81; break;} case "r": {code = 82; break;}
					case "s": {code = 83; break;} case "t": {code = 84; break;} case "u": {code = 85; break;}
					case "v": {code = 86; break;} case "w": {code = 87; break;} case "x": {code = 88; break;}
					case "y": {code = 89; break;} case "z": {code = 90; break;} case "0": {code = 48; break;}
					case "1": {code = 49; break;} case "2": {code = 50; break;} case "3": {code = 51; break;}
					case "4": {code = 52; break;} case "5": {code = 53; break;} case "6": {code = 54; break;}
					case "7": {code = 55; break;} case "8": {code = 56; break;} case "9": {code = 57; break;}
				}
			}
			
			return code;
		}
	}
}

class PressListenerGroup
{
	//--------------------------------------------------------------------------
	//
	//  Properties
	//
	//--------------------------------------------------------------------------
	
	private var _listeners:Array;
	private var _listenersNum:uint;
	
	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------
	
	public function PressListenerGroup()
	{
		_listeners = [];
	}
	
	//--------------------------------------------------------------------------
	//
	//  Methods
	//
	//--------------------------------------------------------------------------
	
	public function addListener(listener:Function):void
	{
		_listenersNum++;
		_listeners.push(listener);
	}
	
	public function removeListener(listener:Function):void
	{
		for (var i:uint = 0; i < _listenersNum; i++)
		{
			if (_listeners[i] == listener)
			{
				_listeners.splice(i, 1);
				_listenersNum--;
				i--;
			}
		}
	}
	
	public function clearListeners():void
	{
		_listeners = [];
	}
	
	public function call():void
	{
		for (var i:uint = 0; i < _listenersNum; i++)
		{
			_listeners[i]();
		}
	}
}